#include "main.hpp"

int main(int argc, char* argv[]) {
    // To keep track of elapsed time.
    clock_t fp_start, sp_start;
    // Start the clock.
    fp_start = clock();
#ifdef USE_MPI
    // Before initializing MPI, start a block so that the mpi is finalized
    // at the end of the block and we can use the end time precisely.
    {
    // Initialize MPI.
    boost::mpi::environment env(argc, argv);
    boost::mpi::communicator world;
    Globals::rank = world.rank(), Globals::size = world.size();
#else
    Globals::rank = 0, Globals::size = 1;
#endif


    // Parse command line arguments.
    try{
      TCLAP::CmdLine cmd("Help message for using parallel CLOSET+ implementation.",
              ' ', "1.0");
      TCLAP::ValueArg<int> nt("t", "transaction", "Total number of transactions\
to proccess", true, 0, "integer", cmd);
      TCLAP::ValueArg<int> ms("s", "minSupport", "Minimum support value for each item (in terms of number of transactions and not in percentage)", true, 1, "integer", cmd);
      TCLAP::ValueArg<string> ofile("o", "output", "Output filename for results. Default: output.txt", false, "output.txt", "string", cmd);
      TCLAP::ValueArg<string> ifile("i", "input", "Input filename for reading transactions. Default: input.txt", false, "input.txt", "string", cmd);
      // pruneSorted: The transactions should be pruned (those lower than min
      // support removed) after sorting (according to support) is done.
      // pruneUnsorted: The transactions should be pruned (those lower than 
      // min support removed) after sorting (according to support) is done.
      TCLAP::ValueArg<int> pm("p", "pruneMethod", "Prune method for the transactions. To prune transactions after sorting use 1; use 2 for pruning before sorting x-acts. Default: 1 (pruneSorted)", false, 1, "1 or 2", cmd);

      TCLAP::ValueArg<int> dt("d", "dataType", "Data type of the input file. \
Use 1 for plain old \"transaction items on each line\" type of file and 2 for \
IBM quest type of data. Default: 2 (IBM Quest datatype)", 
                              false, 2, "1 or 2", cmd);

      TCLAP::SwitchArg vbs("v", "verbose", "Spit more output than usual. Good for debugging.");
      cmd.add(vbs);
      TCLAP::SwitchArg rab("r", "reduceAndBroadcast", "This controls the method local counts are reduced. Related with MPI. The other method is all reduce.");
      TCLAP::SwitchArg ar("a", "allReduce", "This controls the method local counts are reduced. Related with MPI. The other method is reduce and broadcast.");

      // Add prune sorted and unsorted by xor'ing them.
      cmd.xorAdd(rab, ar);
      // Get the arguments now.
      cmd.parse(argc, argv);

      // XOR arguments.
      if (rab.isSet())
          Globals::reduceBroadcast = 1;

      // Other arguments.
      Globals::pruneMethod = pm.getValue();
      if (Globals::pruneMethod != 1 && Globals::pruneMethod != 2) {
          cout << "\nPrune method can only take values 1 or 2. To prune " << 
              "transactions after sorting use 1 and use 2 otherwise." << "\n" << endl;
          return -1;
      }
      Globals::totalTransactions = nt.getValue();
      Globals::minimumSupport = ms.getValue();
      Globals::outputFileName = ofile.getValue();
      Globals::inputFileName = ifile.getValue();
      Globals::dataType = dt.getValue();
      Globals::verbose = vbs.getValue();
    } catch (TCLAP::ArgException &e) {
      // Any exceptions related with command line parsing.
      cerr << "error: " << e.error() << " for arg " << e.argId() << endl;
      return 0; 
    }
     
    // Find the start line and the number of transactions to read.
    FirstPhase fp;

    // Run the first phase of the algorithm.
    fp.run();
    // We have merged fp trees in every node. Start the second phase. First give
    // back any memory we don't need.
    fp.clear();

    if (Globals::verbose) {
        cout << "\nRank " << Globals::rank << ": First Phase finished -\t"
            << (clock() - fp_start) / double (CLOCKS_PER_SEC) << " s.\n\n" << endl;
        sp_start = clock();
    }

    SecondPhase sp(fp);
    sp.run();

    if (Globals::verbose) {
        cout << "Rank " << Globals::rank << ": Second Phase finished -\t"
            << (clock() - sp_start) / double (CLOCKS_PER_SEC) << " s.\n\n" << endl;

        cout << "In total:\t"
        << (clock() - fp_start) / double (CLOCKS_PER_SEC) << " s.\n\n" << endl;
    }
#ifdef USE_MPI
    // If MPI was used, MPI_Finalize it by ending the block and destroying the
    // environment variable used to initialize MPI.
    }
#endif

    // MPI must have finalized when control reaches here.
    if (!Globals::verbose && Globals::rank == 0) {
        // Print the support value, in percentage.
        cout << (double) Globals::minimumSupport / Globals::totalTransactions
             << " " << (clock() - fp_start) / double (CLOCKS_PER_SEC) << endl;
    }
    return 0;
}

